////////////////////////////////////////////////////////////////////////////////////////
//
// JSimpleSim - A machine language simulator based on SimpSim by Anne-Gert Bultena.
//
// Copyright (C) 2008 Gerardo Camorlinga, Ryan Fitzgerald, and RJ Gordon
//
// This file is part of JSimpleSim.
//
// JSimpleSim is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// JSimpleSim is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with JSimpleSim.  If not, see <http://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////////////////////

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package jsimplesim.view;

import javax.swing.DefaultListModel;
import jsimplesim.model.MemHexTableModel;
import org.apache.log4j.Logger;

/**
 *
 * @author f08maroon
 */
public class Disassemble {

    static Logger logger = Logger.getLogger(Disassemble.class);
    private static final String HEX = "0123456789ABCDEF";
    private static DefaultListModel list;
    //private static JSimpleSimView view;

    /**
     *
     * @param model
     * @param view
     */
    public static void run(MemHexTableModel model, JSimpleSimView view) {
        logger.debug("###Entered the disassembler...");
        String hexString = "";
        String hexString2 = "";
        int listIndex = 0;
        int memIndex = 0;
        //view.getListModel().clear();
        if (model != null) {
            logger.debug("####Beginning the disassembly");
            for (int i = 0; i < 16; i++) {

                for (int j = 1; j < 17; j += 2) {

                    memIndex = (i * 16) + j - 1;
                    hexString = (String) model.getValueAt(i, j);
                    hexString2 = (String) model.getValueAt(i, j + 1);

                    view.disOut(disStr(hexString, hexString2, memIndex), listIndex);
                    listIndex++;
                }

            }
        }
    }

    private static String disStr(String hexString, String hexString2, int index) {
        
        String finalStr = "";

        finalStr = byteToHex(index) + ":    ";

        String opCode = hexString.substring(0, 1);
        String arg1 = hexString.substring(1);
        String arg2 = hexString2.substring(0, 1);
        String arg3 = hexString2.substring(1);

        finalStr += hexString + "," + hexString2 + "    ";

        finalStr += determineAssemblyText(opCode, arg1, arg2, arg3);

        return finalStr;
    }

    private static String determineAssemblyText(String opCode, String arg1, String arg2, String arg3) {

        String str = "";
        switch (opCode.charAt(0)) {

        	// Team Alpha edit:
        	// moved the logger statement in each case to be BEFORE the call to the method
            case '1':
                //load mem location to reg
                logger.debug("Case 1 entered");
                str = regMemLoad(arg1, arg2, arg3);
                break;
            case '2':
                //load a value into reg
                //load bc into a
                logger.debug("Case 2 entered");
                str = directMemLoad(arg1, (arg2 + arg3));
                break;
            case '3':
                //store register into mem location
                logger.debug("Case 3 entered");
                str = storeRegMem(arg1, arg2, arg3);
                break;
            case '4':
                // move register R value into register S value
                logger.debug("Case 4 entered");
                str = moveRegReg(arg2, arg3);
                break;
            case '5':
                // integer addition
                logger.debug("Case 5 entered");
                str = addInt(arg1, arg2, arg3);
                break;
            case '6':
            	/* Team Alpha edit:
            	 * removed float addition, replacing with overloaded stack op-codes
            	 * previous code preserved within this comment
                //float addition
                str = addFloat(arg1, arg2, arg3);
                */
            	// Overloaded op-code
                logger.debug("Case 6 entered; overloaded code (arg1) next:");
                switch (arg1.charAt(0)) {
                	case '0':
            			// function call
                        logger.debug("Overloaded Case 0 entered");
                        str = call(arg2 + arg3);
                        break;
            		case '1':
            			// function return
                        logger.debug("Overloaded Case 1 entered");
                        str = ret();
                        break;
                	case '2':
            			// MACRO call that performs boilerplate frame setup
                        logger.debug("Overloaded Case 2 entered");
                        str = mcall(arg2 + arg3);
                        break;
                	case '3':
            			// MACRO return that performs boilerplate frame teardown
                        logger.debug("Overloaded Case 3 entered");
                        str = mret();
                        break;
                	case '4':
            			// push register contents to stack
                        logger.debug("Overloaded Case 4 entered");
                        str = push(arg3);
                        break;
                	case '5':
            			// pop memory at Stack Pointer into register
                        logger.debug("Overloaded Case 5 entered");
                        str = pop(arg3);
                        break;
                	case '6':
            			// relative load from Stack Pointer
                        logger.debug("Overloaded Case 6 entered");
                        str = stackRelLoad(arg2 + arg3);
                        break;
                	case '7':
            			// relative load from Base Pointer
                        logger.debug("Overloaded Case 7 entered");
                        str = baseRelLoad(arg2 + arg3);
                        break;
                	case '8':
                		// not implemented
                	case '9':
                		// not implemented
                	case 'A':
                		// not implemented
                	case 'B':
                		// not implemented
                	case 'C':
                		// not implemented
                	case 'D':
                		// not implemented
                	case 'E':
                		// not implemented
                	case 'F':
            			// not implemented
                        logger.debug("Error! Overload Case 8-F entered! Not implemented!");
            			break;
                	default:
                        // invalid instr, so break cycle
                        logger.debug("Error: invalid instruction found in disassembly. op: " + opCode + " arg1: " + arg1 + " arg2: " + arg2 + " arg3: " + arg3);
                        str = invalid();
                        break;
                }
                break;
            case '7':
                // bitwise OR
                logger.debug("Case 7 entered");
                str = bitOr(arg1, arg2, arg3);
                break;
            case '8':
                // bitwise AND
                logger.debug("Case 8 entered");
                str = bitAnd(arg1, arg2, arg3);
                break;
            case '9':
                // bitwise XOR
                logger.debug("Case 9 entered");
                str = bitXor(arg1, arg2, arg3);
                break;
            case 'A':
                // rotate right, right shift
                logger.debug("Case A entered");
                str = rotateRight(arg1, arg3);
                break;
            case 'B':
                // jmpe or straight jump, compares to reg 0
                logger.debug("Case B entered");
                str = jmpEq(arg1, arg2 + arg3);
                break;
            case 'C':
                //halt; break;
                logger.debug("Case C entered");
                str = halt();
                break;
            case 'D':
                // indirect load, specify memory location through another register
                logger.debug("Case D entered");
                str = indLoad(arg2, arg3);
                break;
            case 'E':
                // store a register into mem location specified by register
                logger.debug("Case E entered");
                str = indStore(arg2, arg3);
                break;
            case 'F':
                // jmple
                logger.debug("Case F entered");
                str = jmpLe(arg1, arg2 + arg3);
                break;
            default:
                // invalid instr, so break cycle
            	// Team Alpha edit:
            	// added logger statement
                logger.debug("Error: invalid instruction found in disassembly. op: " + opCode + " arg1: " + arg1 + " arg2: " + arg2 + " arg3: " + arg3);
                str = invalid();
                break;
        }


        return str;
    }

    private static String bitAnd(String arg1, String arg2, String arg3) {
        
        String str = "AND R" + arg1 + ", R" + arg2 + ", R" + arg3 + '\t' + ";...";
        return str;
    }

    private static String bitOr(String arg1, String arg2, String arg3) {
        
        String str = "OR R" + arg1 + ", R" + arg2 + ", R" + arg3 + '\t' + ";...";
        return str;
    }

    private static String bitXor(String arg1, String arg2, String arg3) {
        
        String str = "XOR R" + arg1 + ", R" + arg2 + ", R" + arg3 + '\t' + ";...";
        return str;
    }

    private static String halt() {
        
        String str = "halt" + '\t' + ";...";
        return str;
    }

    private static String indLoad(String arg2, String arg3) {
        
        String str = "load R" + arg2 + ", [R" + arg3 + "]" + '\t' + ";...";
        return str;
    }

    private static String indStore(String arg2, String arg3) {
        
        String str = "store R" + arg2 + ",[R" + arg3 + "]" + '\t' + ";...";
        return str;
    }

    private static String invalid() {
        
        String str = "invalid";
        return str;
    }

    private static String jmpEq(String arg1, String string) {
        
        String str = "jmpEQ R" + arg1 + "=R0, " + "0x" + string + '\t' + ";...";
        return str;
    }

    private static String jmpLe(String arg1, String string) {
        
        String str = "jmpLE R" + arg1 + "<=R0, " + "0x" + string + '\t' + ";...";
        return str;
    }

    private static String moveRegReg(String arg2, String arg3) {
        
    	// Team Alpha edit:
    	// the two register arguments were reversed, fixed it
        String str = "move R" + arg3 + ", R" + arg2 + '\t' + ";...";
        return str;
    }

    private static String regMemLoad(String arg1, String arg2, String arg3) {
        
        String str = "load R" + arg1 + ", [0x" + arg2 + arg3 + "]" + '\t' + ";...";
        return str;
    }

    private static String rotateRight(String arg1, String arg3) {
        
    	// Team Alpha edit:
    	// added hex to int to the display for clarity (the instruction is usually written in decimal rotate values)
        String str = "ror R" + arg1 + ", 0x" + arg3 + '\t' + ";..." + "0x" + arg3 + "=" + Integer.parseInt(arg3, 16);
        return str;
    }

    private static String storeRegMem(String arg1, String arg2, String arg3) {
        
        String str = "store R" + arg1 + ", [0x" + arg2 + arg3 + "]" + '\t' + ";...";
        return str;
    }

    private static String addInt(String arg1, String arg2, String arg3) {
        
        String str = "addi R" + arg1 + ", R" + arg2 + ", R" + arg3 + '\t' + ";...";
        return str;
    }

    private static String directMemLoad(String arg1, String string) {
        
        String str = "load R" + arg1 + ", 0x" + string + '\t' + ";..." + "0x" + string + "=" + Integer.parseInt(string, 16);
        return str;
    }

    @SuppressWarnings("unused")
	private static String addFloat(String arg1, String arg2, String arg3) {
        
        String str = "addf R1" + arg1 + ", R" + arg2 + ", R" + arg3 + '\t'+ ";...";
        return str;
    }


    /*
     * Spring 2012 Team Alpha
     * Additional op-code methods by Bartis Hawley-Wall
     */
    
    /**
     * simple string builder for disassembly display of instruction: call
     * 
	 * @param location
	 * @return
     */
	private static String call(String location) {
        
        String str = "call instruction at [0x" + location + "]\t;...";
        return str;
    }
    
    /**
     * simple string builder for disassembly display of instruction: ret
     * 
	 * @return
     */
	private static String ret() {
        
        String str = "ret\t;...";
        return str;
    }
    
    /**
     * simple string builder for disassembly display of instruction: mcall
     * 
	 * @param location
	 * @return
     */
	private static String mcall(String location) {
        
        String str = "MACRO call instruction at [0x" + location + "]\t;...";
        return str;
    }
    
    /**
     * simple string builder for disassembly display of instruction: mret
     * 
	 * @return
     */
	private static String mret() {
        
        String str = "MACRO ret\t;...";
        return str;
    }
	
	/**
     * simple string builder for disassembly display of instruction: push
	 * 
	 * @param register
	 * @return
	 */
	private static String push(String register) {
        
        String str = "push R" + register + "\t;...";
        return str;
    }
	
	/**
     * simple string builder for disassembly display of instruction: pop
	 * 
	 * @param register
	 * @return
	 */
	private static String pop(String register) {
        
        String str = "pop R" + register + "\t;...";
        return str;
    }
	
	/**
     * simple string builder for disassembly display of instruction: srload
	 * 
	 * @param offset
	 * @return
	 */
	private static String stackRelLoad(String offset) {
        
        String str = "srload 0x" + offset + "\t;...0x" + offset + "=" + Integer.parseInt(offset, 16);
        return str;
    }
	
	/**
     * simple string builder for disassembly display of instruction: brload
	 * 
	 * @param offset
	 * @return
	 */
	private static String baseRelLoad(String offset) {
        
        String str = "brload 0x" + offset + "\t;...0x" + offset + "=" + Integer.parseInt(offset, 16);
        return str;
    }
    /*
     * Spring 2012 Team Alpha
     * end of additional overloaded op-codes
     */
    
    /**
     * Changes an int into a string.
     * 
     * @param value The int to convert.
     * @return The string representation of the int.
     */
    @SuppressWarnings("unused")
	private static String intToHex(int value) {
        int hi = (value & 0xFF00) >> 8;
        int lo = (value & 0x00FF);
        return byteToHex(hi) + byteToHex(lo);
    }

    /**
     * Changes a byte into a string.
     * 
     * @param value The byte to convert.
     * @return The string representation of the byte.
     */
    private static String byteToHex(int value) {
        int hi = (value & 0xF0) >> 4;
        int lo = (value & 0x0F);
        return "" + HEX.charAt(hi) + HEX.charAt(lo);
    }

    /**
     *
     * @param listModel
     * @return
     */
    public static String createDisassemblyText(DefaultListModel listModel) {

        int size = listModel.getSize();
        int timesInvalid = 0;
        String returnStr = "";

        for (int i = 0; i < size; i++) {
            String line = listModel.get(i) + "";
            // line will look like: (this.byteToHex(count)) +
            // ":    00,00    invalid")
            if (line.contains("00,00") && line.contains("invalid")) {
                // invalid line
                timesInvalid++;
                if (timesInvalid == 0 || timesInvalid == 1) {
                    // line remains the same
                    returnStr += line + "\n";
                } else if (timesInvalid == 2 || timesInvalid == 3) {
                    // line is "
                    line = "                \"";
                    returnStr += line + "\n";
                } else if (timesInvalid > 3) {
                    // line does not show up
                } else {
                    // it's negative??
                }
            } else {
                returnStr += line + "\n";
            }

        }
        return returnStr;
    }

	/**
	 * @param list the list to set
	 */
	public static void setList(DefaultListModel list) {
		Disassemble.list = list;
	}

	/**
	 * @return the list
	 */
	public static DefaultListModel getList() {
		return list;
	}
}
